use crate::util::aes_util;
use crate::util::bytes;
use crate::base::KEY;
use crate::base;
use std::error::Error;
use crate::common::base_file::{ImportSecret, BaseFile};
use crate::server::base_dao;
use crate::common::err::ServerError;

/// 导入 license 码加时
pub fn import_secret(secret: &str) -> Result<(), ServerError> {
    match decrypt(secret) {
        Ok(json) => {
            match serde_json::from_str::<ImportSecret>(&json) {
                Ok(is) => {
                    let mut current = base::current_base();
                    if current.imported().contains(&secret.to_string()) {
                        Err(ServerError::new("secret licence has been used".to_string()))
                    } else if current.mac_address().ne(is.mac_address()) {
                        Err(ServerError::new("mac address is not matching".to_string()))
                    } else {
                        let mut bf = BaseFile::new(
                            current.total() + is.time(),
                            current.used(),
                            is.level(),
                            base::MAC_ADDRESS.as_str(),
                        );
                        bf.imported().append(current.imported());
                        bf.imported().push(secret.to_string());
                        base_dao::write_file(&bf);
                        current.exchange(bf);
                        Ok(())
                    }
                }
                Err(e) => { Err(ServerError::new(e.to_string())) }
            }
        }
        Err(e) => { Err(ServerError::new(e.to_string())) }
    }
}

pub fn init_base(day: usize, level: usize, mac_address: &str) {
    let base_file = BaseFile::new(day * 24 * 60 * 60, 0, level, mac_address);
    base_dao::init_file(&base_file);
}

pub fn create_import_secret(day: usize, level: usize, mac_address: &str) -> String {
    let secret = ImportSecret::new(day * 24 * 60 * 60, level, mac_address);
    encrypt(&secret.to_json())
}

pub fn decrypt(s: &str) -> Result<String, Box<dyn Error>> {
    let mut sp = s.split('.');
    let salt_str = sp.next().ok_or("incorrect code!")?;
    let mut salt = [0u8; 12];
    let salt_v = bytes::hex_vu8(salt_str)?;
    salt[..12].clone_from_slice(&salt_v[..12]);
    let payload_str = sp.next().ok_or("incorrect code!")?;
    let payload_v = bytes::hex_vu8(payload_str)?;
    let result_v = aes_util::decrypt(&KEY, &salt, &payload_v);
    Ok(String::from_utf8(result_v)?)
}

pub fn encrypt(payload: &str) -> String {
    let salt = bytes::rand_u8x12();
    let encoded = aes_util::encrypt(&KEY, &salt, payload.as_bytes());

    let salt_s = bytes::vu8_hex(&salt);
    let encoded_s = bytes::vu8_hex(&encoded);
    format!("{}.{}.", salt_s, encoded_s)
}


#[test]
fn test_encrypt() {
    let result = encrypt("test_this_encrypt_and_decrypt...thx!");
    println!("{:?}", result);
}

#[test]
fn test_decrypt() {
    let result = decrypt("0b10d70710af01802a0bb0d606707c0150bc.02a0db0be0fe0ca00900405d0480e90e102d06e03403a0350130430a201105106c0cc0cd0650ea0b60d004e0460650410a106907f0ac0530310a307404f0c500d0920110730f305e00a0a8054063");
    println!("{:?}", result);
}

#[tokio::test]
async fn test_import_secret() {
    let secret = "03b09f0560c00990b604905506600107d085.0220da05e0a70f10c109606b0dc0000b30b20330b502708805d01502a0fb02904702b03f09d08c0db0890770e60a70f60fc0a70de012070.";
    let success = import_secret(secret);
    println!("{:?}", success);
}

#[test]
fn test_init_base() {
    init_base(20, 1, "00:FF:7E:DC:55:0E");
}